package org.aceor.mddal.client.jdbc;

import com.google.common.collect.Lists;
import org.aceor.mddal.client.jdbc.adapter.AbstractStatementAdapter;
import org.aceor.mddal.common.sqlparser.result.SQLParsedResult;
import org.aceor.mddal.gateway.executor.ExecuteContext;
import org.aceor.mddal.gateway.executor.StatementExecutor;
import org.aceor.mddal.gateway.executor.StatementExecutor.StatementEntity;
import org.aceor.mddal.gateway.router.RealSqlContext;
import org.aceor.mddal.gateway.router.Router;
import org.aceor.mddal.gateway.sqlparser.SQLParserFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * Created by lxue on 16/4/5.
 */
public class StatementFacade extends AbstractStatementAdapter implements Statement {

    private static final Logger log = LoggerFactory.getLogger(StatementFacade.class);

    protected final DataSourceFacade dataSourceFacade;
    protected final ConnectionFacade connectionFacade;

    public StatementFacade(DataSourceFacade dataSourceFacade, ConnectionFacade connectionFacade) {
        this.dataSourceFacade = dataSourceFacade;
        this.connectionFacade = connectionFacade;
    }

    @Override
    public ResultSet executeQuery(String sql) throws SQLException {
        ResultSet rs = createStatementExecutor(sql).executeQuery();
        return rs;
    }

    @Override
    public int executeUpdate(String sql) throws SQLException {
        return createStatementExecutor(sql).executeUpdate();
    }

    @Override
    public boolean execute(String sql) throws SQLException {
        return createStatementExecutor(sql).execute();
    }

    @Override
    public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
        return createStatementExecutor(sql).executeUpdate(autoGeneratedKeys);
    }

    @Override
    public int executeUpdate(String sql, int[] columnIndexes) throws SQLException {
        return createStatementExecutor(sql).executeUpdate(columnIndexes);
    }

    @Override
    public int executeUpdate(String sql, String[] columnNames) throws SQLException {
        return createStatementExecutor(sql).executeUpdate(columnNames);
    }

    @Override
    public boolean execute(String sql, int autoGeneratedKeys) throws SQLException {
        return createStatementExecutor(sql).execute(autoGeneratedKeys);
    }

    @Override
    public boolean execute(String sql, int[] columnIndexes) throws SQLException {
        return createStatementExecutor(sql).execute(columnIndexes);
    }

    @Override
    public boolean execute(String sql, String[] columnNames) throws SQLException {
        return createStatementExecutor(sql).execute(columnNames);
    }

    private StatementExecutor createStatementExecutor(final String sql) throws SQLException {
        /**
         * 获取路由信息.主要是分库分表的规则
         */
        Router router = Router.getInstance(dataSourceFacade.getLogicTable());
        /**
         * sql解析
         */
        SQLParsedResult sqlParsedResult = SQLParserFactory
                .createSQLParserEngine(sql, null, router.getAllTableShardingColumnsMapping())
                .parse();

        /**
         * 通过路由计算得到执行上下文
         */
        ExecuteContext executeContext = router.route(sqlParsedResult, sql, null);
        log.info("ExecuteContext :{}", executeContext);

        /**
         * 计算sql与对应执行的Statement
         */
        Collection<StatementEntity> statementEntities = buildStatementEntities(executeContext);

        return StatementExecutor
                .build()
                .setExecuteContext(executeContext)
                .setStatementEntities(statementEntities)
                .create();
    }

    private Collection<StatementEntity> buildStatementEntities(ExecuteContext executeContext) throws SQLException {
        Collection<StatementEntity> statementEntities = Lists.newLinkedList();
        for (Map.Entry<String, List<RealSqlContext>> entry : executeContext.sqlMap.entrySet()) {
            Statement statement = connectionFacade.getConnection(entry.getKey()).createStatement();
            for (RealSqlContext realSqlContext : entry.getValue()) {
                statementEntities.add(new StatementEntity(realSqlContext.sql, statement));
            }
        }
        return statementEntities;
    }


    @Override
    public ResultSet getResultSet() throws SQLException {
        return null;
    }

    @Override
    public void close() throws SQLException {

    }
}
